package org.apache.ocean.main.util;

import java.io.IOException;

import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.OpenBitSet;

/**
 * <code>BitDocSet</code> represents an unordered set of Lucene Document Ids
 * using a BitSet. A set bit represents inclusion in the set for that document.
 * 
 */
public class BitDocSet implements DocSet { // extends DocSetBase {
	final OpenBitSet bits;
	int size; // number of docs in the set (cached for perf)

	public BitDocSet(int size) {
		bits = new OpenBitSet(size);
	}

	public BitDocSet() {
		bits = new OpenBitSet();
	}

	public DocSet intersection(DocSet other) {
		OpenBitSet newbits = (OpenBitSet) (this.getBits().clone());
		newbits.and(other.getBits());
		return new BitDocSet(newbits);
	}

	/**
	 * Construct a BitDocSet. The capacity of the OpenBitSet should be at least
	 * maxDoc()
	 */
	public BitDocSet(OpenBitSet bits) {
		this.bits = bits;
		size = -1;
	}

	/**
	 * Construct a BitDocSet, and provides the number of set bits. The capacity of
	 * the OpenBitSet should be at least maxDoc()
	 */
	public BitDocSet(OpenBitSet bits, int size) {
		this.bits = bits;
		this.size = size;
	}

	public DocIdSetIterator iterator() {
		return bits.iterator();
	}

	/**
	 * public DocIterator iterator() { return new DocIterator() { private final
	 * BitSetIterator iterator = new BitSetIterator(bits); private int pos =
	 * iterator.next(); public boolean hasNext() { return pos >= 0; }
	 * 
	 * public Integer next() { return nextDoc(); }
	 * 
	 * public void remove() { bits.clear(pos); }
	 * 
	 * public int nextDoc() { int old = pos; pos = iterator.next(); return old; }
	 * 
	 * public float score() { return 0.0f; } }; }
	 */
	/**
	 * 
	 * @return the <b>internal</b> OpenBitSet that should <b>not</b> be
	 *         modified.
	 */
	public OpenBitSet getBits() {
		return bits;
	}

	public void add(int doc) {
		bits.set(doc);
		size = -1; // invalidate size
	}

	public void addUnique(int doc) {
		bits.set(doc);
		size = -1; // invalidate size
	}

	public int size() {
		if (size != -1)
			return size;
		return size = (int) bits.cardinality();
	}

	/**
	 * The number of set bits - size - is cached. If the bitset is changed
	 * externally, this method should be used to invalidate the previously cached
	 * size.
	 */
	public void invalidateSize() {
		size = -1;
	}

	public boolean exists(int doc) {
		return bits.get(doc);
	}

	public int intersectionSize(DocSet other) {
		// if (other instanceof BitDocSet) {
		return (int) OpenBitSet.intersectionCount(this.bits, ((BitDocSet) other).bits);
		// } else {
		// // they had better not call us back!
		// return other.intersectionSize(this);
		// }
	}

	public int unionSize(DocSet other) {
		// if (other instanceof BitDocSet) {
		// if we don't know our current size, this is faster than
		// size + other.size - intersection_size
		return (int) OpenBitSet.unionCount(this.bits, ((BitDocSet) other).bits);
		// } else {
		// they had better not call us back!
		// return other.unionSize(this);
		// }
	}

	public int andNotSize(DocSet other) {
		// if (other instanceof BitDocSet) {
		// if we don't know our current size, this is faster than
		// size - intersection_size
		return (int) OpenBitSet.andNotCount(this.bits, ((BitDocSet) other).bits);
		// } else {
		// return super.andNotSize(other);
		// }
	}

	public DocSet andNot(DocSet other) {
		OpenBitSet newbits = (OpenBitSet) (bits.clone());
		// if (other instanceof OpenBitSet) {
		newbits.andNot(((BitDocSet) other).bits);
		// } else {
		// DocIdSetIterator iterator = other.iterator();
		// while (iterator.next())
		// newbits.set(iterator.doc());
		// }
		return new BitDocSet(newbits);
	}

	public DocSet union(DocSet other) {
		OpenBitSet newbits = (OpenBitSet) (bits.clone());
		// if (other instanceof BitDocSet) {
		newbits.union(((BitDocSet) other).bits);
		// } else {
		// DocIdSetIterator iterator = other.iterator();
		// while (iterator.next()) newbits.set(iterator.doc());
		// }
		return new BitDocSet(newbits);
	}

	public long memSize() {
		return (bits.getBits().length << 3) + 16;
	}
}
